#pragma once

#include "vec.h"

namespace sfc::math::geo {

template <class T>
struct Mat2 {
  Vec2<T> _inn[2] = {};

 public:
  [[sfc_inline]] Mat2() = default;

  [[sfc_inline]] Mat2(const Vec2<T>& v0, const Vec2<T>& v1) : _inn{v0, v1} {}

  [[sfc_inline]] static auto eye() -> Mat2 {
    return {{1, 0}, {0, 1}};
  }

  [[sfc_inline]] auto operator[](usize i, Dummy) const -> Vec2<T> {
    return _inn[i];
  }

  [[sfc_inline]] auto operator[](Dummy, usize j) const -> Vec2<T> {
    return {_inn[0][j], _inn[1][j]};
  }

  [[sfc_inline]] auto operator[](usize i, Dummy) -> T& {
    return _inn[i];
  }

  [[sfc_inline]] auto operator[](usize i, usize j) const -> T {
    return _inn[i][j];
  }

  [[sfc_inline]] auto operator[](usize i, usize j) -> T& {
    return _inn[i][j];
  }

  [[sfc_inline]] auto operator~() const -> Mat2 {
    return Mat2{(*this)[_, 0], (*this)[_, 1]};
  }

  // k*A
  [[sfc_inline]] friend auto operator*(f64 k, const Mat2& A) -> Mat2 {
    return Mat2{k * A[0], k * A[1]};
  }

  // A*v
  [[sfc_inline]] auto operator*(const Vec2<T>& v) const -> Vec2<T> {
    return {_inn[0] * v, _inn[1] * v};
  }

  // v*A
  [[sfc_inline]] friend auto operator*(const Vec2<T>& v, const Mat2& A) -> Vec2<T> {
    return {v * A[_, 0], v * A[_, 1]};
  }

  // A*B
  [[sfc_inline]] auto operator*(const Mat2& B) const -> Mat2 {
    return {_inn[0] * B, _inn[1] * B};
  }

  [[sfc_inline]] auto det() const -> T {
    return _inn[0][0] * _inn[1][1] - _inn[0][1] * _inn[1][0];
  }

  [[sfc_inline]] auto inv() const -> Mat2 {
    const auto k = T{1} / this->det();
    const auto m = Mat2{{+k * _inn[1][1], -k * _inn[0][1]}, {-k * _inn[1][0], +k * _inn[0][0]}};
    return m;
  }

  void fmt(auto& f) const {
    f.write(_inn);
  }
};

using Mat2d = Mat2<f64>;

}  // namespace sfc::math::geo
